// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package storage

import (
	"time"

	"github.com/m3db/m3/src/dbnode/persist/fs/commitlog"
)

// numIntervals returns the number of intervals between [start, end] for a given
// windowSize.
// NB: edge conditions:
// 	- returns 0 if window <= 0 ;
//  - returns 0 if end is before start;
//  - returns 1 if end == start
func numIntervals(startInclusive, endInclusive time.Time, window time.Duration) int {
	if window <= 0 || endInclusive.Before(startInclusive) {
		return 0
	}

	return 1 + int((endInclusive.Sub(startInclusive))/window)
}

// timesInRange returns the points between [start, end] windowSize apart, starting at `end`, in
// reverse chronological order.
// NB: returns empty slice if window <= 0 or end is before start
func timesInRange(startInclusive, endInclusive time.Time, windowSize time.Duration) []time.Time {
	ni := numIntervals(startInclusive, endInclusive, windowSize)
	if ni == 0 {
		return nil
	}
	times := make([]time.Time, 0, ni)
	for t := endInclusive; !t.Before(startInclusive); t = t.Add(-windowSize) {
		times = append(times, t)
	}
	return times
}

// filterCommitLogFiles returns the values in the slice `files` which
// satisfy the provided predicate.
func filterCommitLogFiles(
	files []commitlog.File,
	predicate func(start time.Time, duration time.Duration) (bool, error),
) ([]commitlog.File, error) {
	filtered := make([]commitlog.File, 0, len(files))
	for _, f := range files {
		passed, err := predicate(f.Start, f.Duration)
		if err != nil {
			return nil, err
		}
		if passed {
			filtered = append(filtered, f)
		}
	}
	return filtered, nil
}

// filterTimes returns the values in the slice `times` which satisfy
// the provided predicate.
func filterTimes(times []time.Time, predicate func(t time.Time) bool) []time.Time {
	filtered := make([]time.Time, 0, len(times))
	for _, t := range times {
		if predicate(t) {
			filtered = append(filtered, t)
		}
	}

	return filtered
}
